#!/usr/bin/env ruby
require 'rubygems'
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib')))
require "bundler/setup"
require 'logger'
require 'rdf'
require 'rdf/trig'
require 'rdf/nquads'
require 'ebnf/ll1/parser'
require 'getoptlong'

def run(input, options)
  if options[:profile]
    require 'profiler'
  end

  reader_class = RDF::Reader.for(options[:input_format].to_sym)
  raise "Reader not found for #{options[:input_format]}" unless reader_class

  start = Time.new
  num = 0
  Profiler__::start_profile if options[:profile]
  if options[:output_format] == :nquads || options[:quiet]
    r = reader_class.new(input, options[:parser_options])
    r.each do |statement|
      num += 1
      if options[:errors] && statement.invalid?
        $stderr.puts "Invalid statement at #{r.lineno}: #{statement.inspect}"
      elsif options[:quiet]
        print "." if options[:quiet] == 1
      else
       options[:output].puts statement.to_nquads
      end
    end
  elsif options[:output_format] == :inspect
    reader_class.new(input, options[:parser_options]).each do |statement|
      num += 1
      options[:output].puts statement.inspect
    end
  else
    r = reader_class.new(input, options[:parser_options])
    g = RDF::Repository.new << r
    num = g.count
    options[:output].puts g.dump(options[:output_format], {:prefixes => r.prefixes}.merge(options[:writer_options]))
  end
  if options[:profile]
    Profiler__::stop_profile
    Profiler__::print_profile($stderr)
  end
  puts
  secs = Time.new - start
  puts "Parsed #{num} statements in #{secs} seconds @ #{num/secs} statements/second."
rescue Exception => e
  fname = input.respond_to?(:path) ? input.path : "-stdin-"
  STDERR.puts("Error in #{fname}: #{e.message}")
  STDERR.puts "Backtrace: " + e.backtrace.join("\n  ")
  raise e
end

logger = Logger.new(STDERR)
logger.level = Logger::WARN
logger.formatter = lambda {|severity, datetime, progname, msg| "#{severity}: #{msg}\n"}

parser_options = {
  base_uri:  nil,
  validate:  false,
  logger: logger,
}

writer_options = {
  base_uri:  nil,
  standard_prefixes:  true,
  logger: logger,
}

options = {
  parser_options:  parser_options,
  writer_options:  writer_options,
  output:  STDOUT,
  output_format:  :nquads,
  input_format:  :trig,
}
input = nil

opts = GetoptLong.new(
  ["--dbg", GetoptLong::NO_ARGUMENT],
  ["--base", GetoptLong::REQUIRED_ARGUMENT],
  ["--errors", GetoptLong::NO_ARGUMENT],
  ["--execute", "-e", GetoptLong::REQUIRED_ARGUMENT],
  ["--canonicalize", GetoptLong::NO_ARGUMENT],
  ["--freebase", GetoptLong::NO_ARGUMENT],
  ["--format", GetoptLong::REQUIRED_ARGUMENT],
  ["--input-format", GetoptLong::REQUIRED_ARGUMENT],
  ["--output", "-o", GetoptLong::REQUIRED_ARGUMENT],
  ["--profile", GetoptLong::NO_ARGUMENT],
  ["--progress", GetoptLong::NO_ARGUMENT],
  ["--quiet", GetoptLong::NO_ARGUMENT],
  ["--validate", GetoptLong::NO_ARGUMENT],
  ["--verbose", GetoptLong::NO_ARGUMENT]
)
opts.each do |opt, arg|
  case opt
  when '--dbg'          then logger.level = Logger::DEBUG
  when '--base'         then parser_options[:base_uri] = writer_options[:base_uri] = arg
  when '--canonicalize' then parser_options[:canonicalize] = true
  when '--errors'       then options[:errors] = true
  when '--execute'      then input = arg
  when '--format'       then options[:output_format] = arg.to_sym
  when '--freebase'     then parser_options[:freebase] = true
  when '--input-format' then options[:input_format] = arg.to_sym
  when '--output'       then options[:output] = File.open(arg, "w")
  when '--profile'      then options[:profile] = true
  when '--progress'     then logger.level = Logger::INFO
  when '--quiet'
    options[:quiet] = options[:quiet].to_i + 1
    logger.level = Logger::FATAL
  when '--stream'       then writer_options[:stream] = true
  when '--validate'     then parser_options[:validate] = true
  when '--verbose'      then $verbose = true
  end
end

if ARGV.empty?
  s = input ? input : $stdin.read
  run(StringIO.new(s), options)
else
  ARGV.each do |test_file|
    run(Kernel.open(test_file), options)
  end
end
puts
